const PADDING = 'PADDING'
const FULLFILLED = 'FULLFILLED'
const REJECTED = 'REJECTED'
class Promise {
    constructor(executor) {
        this.state = PADDING
        this.value = undefined // fullfilled  result
        this.reason = undefined // rejected  result
        this.onfullfilledCallbacks = []
        this.onrejectCallbacks = []
        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }
    resolve = (value) => {
        if (this.state === PADDING) {
            this.state = FULLFILLED
            this.value = value
        }
        this.onfullfilledCallbacks.forEach(fn => fn())
    }
    reject = (reason) => {
        if (this.state === PADDING) {
            this.state = REJECTED
            this.reason = reason
        }
        this.onrejectCallbacks.forEach(fn => fn())
    }
    then = (fullfilled, rejected) => {
        let promise2 = new Promise((resolve, reject) => {
            // judge state execute callback
            if (this.state === FULLFILLED) {
                setTimeout(() => {
                    try {
                        let x = fullfilled(this.value)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0);
            }

            if (this.state === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = rejected(this.reason)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            }

            if (this.state === PADDING) {
                this.onfullfilledCallbacks.push(() => {
                    try {
                        let x = fullfilled(this.value)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
                this.onrejectCallbacks.push(() => {
                    try {
                        let x = rejected(this.reason)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
        })

        return promise2
    }
    resolvePromise = (promise2, x, resolve, reject) => {
        if (promise2 === x) {
            reject(new TypeError('no-same'))
        }
        if (typeof x === 'object' && x !== null || typeof x === "function") {
            try {
                const then = x.then
                let called = false
                if (then) {
                    if (typeof then === 'function') {
                        then.call(x, (y) => {
                            this.resolvePromise(promise2, y, resolve, reject)
                        }, (r) => {
                            if (called) return
                            called = true
                            reject(r)
                        })
                    } else {
                        if (called) return
                        called = true
                        resolve(x)
                    }
                } else {
                    if (called) return
                    called = true
                    resolve(x)
                }
            } catch (error) {
                if (called) return
                called = true
                reject(error)
            }
        } else {
            resolve(x)
        }
    }
}
module.exports = Promise